package lixp.redis.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lixp.dao.mapper.CmsArticleMapper;
import lixp.pojo.CmsArticle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;

import java.util.List;

import static java.lang.System.out;


/**
 * 进行分页查询的时候，缓存中的key和单个查询、修改的key不一致，因此进行完单个修改的操作后，分页查询的数据不会改
 * 解决方式一（一般采用的方式）：
 * 一般情况一张表里的数据都放在一个缓存空间：cacheNames：lixp
 * 一旦发生增删改的操作，就把整个表的空间的数据全部更新
 *
 * @CacheEvict(cacheNames = "lixp",allEntries=true)
 * 解决方式二（本项目采用的方式）：
 * 将分页查询的数据和单个数据的操作的数据放在不同的空间
 * 进行完单个数据的增删改操作后，直接删除分页数据所在的空间
 */
@Service
@CacheConfig( cacheNames = "cmsArticle" ) //redis 的存储空间，一般情况一张表 的数据放在一个存储空间中
public class RedisService {
    @Autowired
    CmsArticleMapper cmsArticleMapper;

    /*
     *   运行流程：
     *   @Cacheable：
     *   1、方法运行之前，先去查询Cache（缓存组件），按照cacheNames指定的名字获取；
     *      （CacheManager先获取相应的缓存），第一次获取缓存如果没有Cache组件会自动创建。
     *   2、去Cache中查找缓存的内容，使用一个key，默认就是方法的参数；
     *      key是按照某种策略生成的；默认是使用keyGenerator生成的，默认使用SimpleKeyGenerator生成key；
     *          SimpleKeyGenerator生成key的默认策略；
     *                  如果没有参数；key=new SimpleKey()；
     *                  如果有一个参数：key=参数的值
     *                  如果有多个参数：key=new SimpleKey(params)；
     *   3、没有查到缓存就调用目标方法；
     *   4、将目标方法返回的结果，放进缓存中
     *
     *   @Cacheable标注的方法执行之前先来检查缓存中有没有这个数据，默认按照参数的值作为key去查询缓存，
     *   如果没有就运行方法并将结果放入缓存；以后再来调用就可以直接使用缓存中的数据；
     *
     *   核心：
     *      1）、使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】组件
     *      2）、key使用keyGenerator生成的，默认是SimpleKeyGenerator
     *
     *
     *   几个属性：
     *      cacheNames/value：指定缓存组件的名字;将方法的返回结果放在哪个缓存中，是数组的方式，可以指定多个缓存；
     *
     *      key：缓存数据使用的key；可以用它来指定。默认是使用方法参数的值  1-方法的返回值
     *              编写SpEL； #i d;参数id的值   #a0  #p0  #root.args[0]
     *              getEmp[2]
     *
     *      keyGenerator：key的生成器；可以自己指定key的生成器的组件id
     *              key/keyGenerator：二选一使用;
     *
     *
     *      cacheManager：指定缓存管理器；或者cacheResolver指定获取解析器
     *
     *      condition：指定符合条件的情况下才缓存；
     *              ,condition = "#id>0"
     *          condition = "#a0>1"：第一个参数的值》1的时候才进行缓存
     *
     *      unless:否定缓存；当unless指定的条件为true，方法的返回值就不会被缓存；可以获取到结果进行判断
     *              unless = "#result == null"
     *              unless = "#a0==2":如果第一个参数的值是2，结果不缓存；
     *      sync：是否使用异步模式
     * @param id
     * @return
     */
    @Cacheable( key = "#id" ) //表示redis中存储的key
    public CmsArticle getCmsArticleById(Integer id) {
        CmsArticle cmsArticle = cmsArticleMapper.selectByPrimaryKey(id);
        return cmsArticle;
    }

    @Cacheable( cacheNames = "page_cmsArticle", key = "#pageNum" )
    public PageInfo findAll(Integer pageNum) {
        PageHelper.startPage(pageNum, 10);
        List<CmsArticle> list = cmsArticleMapper.selectByExample(null);
        PageInfo page = new PageInfo(list, 5);
        return page;
    }

    /**
     * @CachePut：既调用方法，又更新缓存数据；同步更新缓存 修改了数据库的某个数据，同时更新缓存；
     * 运行时机：
     * 1、先调用目标方法
     * 2、将目标方法的结果缓存起来
     */
    @CachePut( key = "#cmsArticle.articleId" )
    @CacheEvict( cacheNames = "page_cmsArticle", allEntries = true )
    public CmsArticle update(CmsArticle cmsArticle) {
        out.println(cmsArticle);
        int i = cmsArticleMapper.updateByPrimaryKeySelective(cmsArticle);
        if (i > 0) {
            return cmsArticle;
        } else {
            return null;
        }

    }

    /**
     * @CacheEvict：缓存清除 key：指定要清除的数据
     * allEntries = true：指定清除这个缓存中所有的数据
     * beforeInvocation = false：缓存的清除是否在方法之前执行
     * 默认代表缓存清除操作是在方法执行之后执行;如果出现异常缓存就不会清除
     * <p>
     * beforeInvocation = true：
     * 代表清除缓存操作是在方法运行之前执行，无论方法是否出现异常，缓存都清除
     */

    @CacheEvict( cacheNames = "page_cmsArticle", allEntries = true )
    public boolean add(CmsArticle cmsArticle) {
        int i = cmsArticleMapper.insert(cmsArticle);
        if (i > 0) {
            return true;
        } else {
            return false;
        }

    }

    // @Caching 定义复杂的缓存规则
//    @Caching(
//            cacheable = {
//                    @Cacheable(/*value="emp",*/key = "#lastName")
//            },
//            put = {
//                    @CachePut(/*value="emp",*/key = "#result.id"),
//                    @CachePut(/*value="emp",*/key = "#result.email")
//            }
//    )

    @Caching( evict = {@CacheEvict( key = "#id" ),
            @CacheEvict( cacheNames = "page", allEntries = true )} )
    public boolean delById(Integer id) {
        int i = cmsArticleMapper.deleteByPrimaryKey(id);
        if (i > 0) {
            return true;
        } else {
            return false;
        }
    }
}
